package br.com.mmm.javacenter.framework.configuration;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import javax.persistence.Entity;
import javax.servlet.ServletContext;

import org.apache.log4j.Logger;

import br.com.mmm.javacenter.framework.annotations.Controller;
import br.com.mmm.javacenter.framework.annotations.Dao;
import br.com.mmm.javacenter.framework.annotations.Domain;
import br.com.mmm.javacenter.framework.annotations.FormatterComponent;
import br.com.mmm.javacenter.framework.annotations.GenericTagSupport;
import br.com.mmm.javacenter.framework.annotations.InjectorComponent;
import br.com.mmm.javacenter.framework.annotations.RenderComponent;
import br.com.mmm.javacenter.framework.entity.EntidadeBase;
import br.com.mmm.javacenter.framework.reflection.injectors.Injector;
import br.com.mmm.javacenter.framework.view.formatters.Formatter;
import br.com.mmm.javacenter.framework.view.taglib.helper.Render;

public class Configuration {
	private static final String WEB_INF_CLASSES = "/WEB-INF/classes/";
	@SuppressWarnings("unused")
	private static final String WEB_INF_LIB = "/WEB-INF/lib/";
	private static final String CLASS_FILE_EXTENSION = ".class";
	private static final String JAR_FILE_EXTENSION = ".jar";
	private ServletContext context;
	private static Logger logger = Logger.getLogger(Configuration.class);

	public Configuration(ServletContext context) {
		this.context = context;
	}
	
	
	public void discoveryClasses(){
		long time = System.currentTimeMillis();
		logger.debug("iniciando busca por objetos do framework.");
		discoveryClasses(WEB_INF_CLASSES);
//		discoveryClasses(WEB_INF_LIB);	
		long endTime = System.currentTimeMillis();
		logger.debug("Tempo total : " + (endTime-time) + "S");
	}

	@SuppressWarnings("unchecked")
	public void discoveryClasses(String directory){
		Set<String> files = context.getResourcePaths(directory);
		if(files!=null)
		for(String file:files){
			File f = new File(context.getRealPath(file));
			if (f.isDirectory()){
				discoveryClasses(file);
			}else if(file.endsWith(CLASS_FILE_EXTENSION)){
				String className = parseClassName(WEB_INF_CLASSES, file);
				registerComponent(className);
			}else if (file.endsWith(JAR_FILE_EXTENSION)){
				try {
					
					discoveryClasses(new ZipFile(f));
				} catch (ZipException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public void discoveryClasses(ZipFile zip){
		Enumeration enumeration = zip.entries();
		while(enumeration.hasMoreElements()){
			ZipEntry entry = (ZipEntry) enumeration.nextElement();
			if (entry.getName().endsWith(CLASS_FILE_EXTENSION)){
				registerComponent(parseClassName(entry.getName()));	
 			}
		}
	} 
	
	@SuppressWarnings("unchecked")
	private void registerComponent(String className) {
		Class<?> klass;
		if (className.endsWith("HibernateUtil")){
			return;
		}
		
		if((klass = classComponent(Dao.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("dao encontrado: " + klass.getName());
				IocController.picoSetterContainer.registerComponentImplementation(klass);
			}
		}
		if((klass = classComponent(Entity.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("entity Bean encontrado: " + klass.getName());
				IocController.picoSetterContainer.registerComponentImplementation(klass);
				IocController.entityBeans.add((Class<? extends EntidadeBase<?>>) klass);
			}
		}
		if((klass = classComponent(GenericTagSupport.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("Menu component encontrado: " + klass.getName());
				IocController.entityBeans.add((Class<? extends EntidadeBase<?>>) klass);
			}
		}
		if((klass = classComponent(Controller.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("controller encontrado: " + klass.getName());
				IocController.picoSetterContainer.registerComponentImplementation(klass);
				String value = ((Controller)klass.getAnnotation(Controller.class)).value();
				if(value.equals("[NOT SETED]")){
					IocController.controllersBeans.put(klass.getSimpleName().substring(0,1).toLowerCase() + klass.getSimpleName().substring(1),klass);
				}else{
					IocController.controllersBeans.put(value,klass);
				}
			}
		}
		if((klass = classComponent(Domain.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("domain encontrado: " + klass.getName());
				IocController.picoSetterContainer.registerComponentImplementation(klass);
			}
		}
		if((klass = classComponent(InjectorComponent.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("componente injector encontrado: " + klass.getName());
				IocController.picoSetterContainer.registerComponentImplementation(klass);
				IocController.injectorsBeans.add((Class<? extends Injector>) klass);
			}
		}
		if((klass = classComponent(FormatterComponent.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("componente formatter encontrado: " + klass.getName());
				IocController.picoSetterContainer.registerComponentImplementation(klass);
				IocController.formattersBeans.add((Class<? extends Formatter>) klass);
			}
		}
		if((klass = classComponent(RenderComponent.class,className)) != null){
			if(Modifier.isPublic(klass.getModifiers())){
				logger.debug("componente render encontrado: " + klass.getName());
				IocController.picoContainer.registerComponentImplementation(klass);
				IocController.rendersBeans.add((Class<? extends Render>) klass);
			}
		}
		
	}


	private Class<?> classComponent(Class<? extends Annotation> annotation,String className){
		Class<?> klass;
		try{
			klass = Class.forName(className);
			if (klass.isAnnotationPresent(annotation)){
				return klass;
			}
		}catch(Throwable e){}
		return null;	
	}
	private String parseClassName(String directory, String name) {
		if (!name.startsWith(directory)) {
			throw new IllegalArgumentException();
		}
		name = name.substring(directory.length(), name.length() - CLASS_FILE_EXTENSION.length());
		name = name.replaceAll("/|\\\\", ".");
		// bug fix for cargo
		name = name.replaceAll("\\.\\.", ".");
		return name;
	}
	private String parseClassName(String name) {
		name = name.substring(0, name.length() - CLASS_FILE_EXTENSION.length());
		name = name.replaceAll("/|\\\\", ".");
		// bug fix for cargo
		name = name.replaceAll("\\.\\.", ".");
		return name;
	}
}